package com.xiaoshuidi.cloud.module.finance.service.withdraw.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.common.util.json.JsonUtils;
import com.xiaoshuidi.cloud.framework.pay.config.PayProperties;
import com.xiaoshuidi.cloud.framework.pay.core.client.PayClient;
import com.xiaoshuidi.cloud.framework.pay.core.client.PayClientFactory;
import com.xiaoshuidi.cloud.framework.pay.core.client.PayCommonResult;
import com.xiaoshuidi.cloud.framework.pay.core.client.impl.tongshangyun.dto.WithdrawApplyNotifyRespDTO;
import com.xiaoshuidi.cloud.framework.pay.core.client.impl.tongshangyun.dto.WithdrawApplyReqDTO;
import com.xiaoshuidi.cloud.framework.pay.core.client.impl.tongshangyun.dto.WithdrawApplyRespDTO;
import com.xiaoshuidi.cloud.module.finance.controller.admin.withdraw.vo.ThirdWithdrawQueryReqVO;
import com.xiaoshuidi.cloud.module.finance.controller.admin.withdraw.vo.ThirdWithdrawRespVO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.FinanceThirdWithdrawDO;
import com.xiaoshuidi.cloud.module.finance.dal.mysql.finance.FinanceThirdWithdrawMapper;
import com.xiaoshuidi.cloud.module.finance.enums.withdraw.TongLianRefundStatusEnum;
import com.xiaoshuidi.cloud.module.finance.enums.withdraw.WithdrawStatusEnum;
import com.xiaoshuidi.cloud.module.finance.service.withdraw.FinanceThirdWithdrawService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.xiaoshuidi.cloud.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.xiaoshuidi.cloud.module.finance.enums.ErrorCodeConstants.WITHDRAW_RECORD_NOT_EXIST;
import static com.xiaoshuidi.cloud.module.finance.enums.ErrorCodeConstants.WITHDRAW_STATUS_ERROR;

@Service
@Validated
@Slf4j
public class FinanceThirdWithdrawServiceImpl implements FinanceThirdWithdrawService {
    @Resource
    private FinanceThirdWithdrawMapper financeThirdWithdrawMapper;

    @Resource
    private PayProperties payProperties;

    @Resource
    private PayClientFactory payClientFactory;

    @Override
    public void notifyWithDraw(WithdrawApplyNotifyRespDTO result) {
        FinanceThirdWithdrawDO dto = financeThirdWithdrawMapper.getByNoAndAmount(result.getBizOrderNo(), result.getAmount());
        if (dto.getStatus().equalsIgnoreCase(WithdrawStatusEnum.SUCCESS.getName())) {
            log.error("提现单号{}已提现到账", result.getBizOrderNo());
            return;
        }
        FinanceThirdWithdrawDO withdrawDO = new FinanceThirdWithdrawDO();
        if (result.getStatus().equalsIgnoreCase("OK")) {
            withdrawDO.setStatus(WithdrawStatusEnum.SUCCESS.getName());
            try {
                withdrawDO.setReceiveDate(strToLocalDateTime(result.getPayDatetime()).toLocalDate());
            } catch (Exception e) {
                log.error("提现到账时间有误！！！！");
            }
            HashMap map = result.getChannelExtendInfo();
            String voucherNo = map.get("voucherNo").toString();
            withdrawDO.setVoucherNo(voucherNo);
            withdrawDO.setThirdWithdrawNo(result.getOrderNo());
        } else if (result.getStatus().equalsIgnoreCase("error")) {
            withdrawDO.setStatus(WithdrawStatusEnum.FAILURE.getName());
        }

        financeThirdWithdrawMapper.updateByNoAndAmount(result.getBizOrderNo(), result.getAmount(), withdrawDO);

    }

    @Override
    public Boolean retryWithdraw(Long id) {
        //校验发起提现的id是否状态是失败状态
        FinanceThirdWithdrawDO withdrawDO = financeThirdWithdrawMapper.selectById(id);
        if (ObjectUtils.isEmpty(withdrawDO)) {
            throw exception(WITHDRAW_RECORD_NOT_EXIST);
        }
        if (!withdrawDO.getStatus().equalsIgnoreCase(WithdrawStatusEnum.FAILURE.getName())) {
            throw exception(WITHDRAW_STATUS_ERROR);
        }
        //重新发起提现
        LocalDateTime now = LocalDateTime.now();
        withdrawDO.setStatus(WithdrawStatusEnum.PROCESS.getName())
                .setWithdrawTime(now);
        financeThirdWithdrawMapper.updateById(withdrawDO);
        try {
            String expireDateTime = now.plusMinutes(10).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            WithdrawApplyReqDTO applyReqDTO = new WithdrawApplyReqDTO()
                    .setBizUserId(withdrawDO.getAccountId().toString())
                    .setAmount(withdrawDO.getBalance())
                    //todo 提现费率 目前写0
                    .setFee(0L)
                    .setBankCardNo(withdrawDO.getBankNo())
                    .setBankCardPro(Long.valueOf(withdrawDO.getAccType()))
                    .setBackUrl(payProperties.getTongshangyunNotifyUrl() + "/" + payProperties.getChannelId())
                    .setBizOrderNo(withdrawDO.getWithdrawNo())
                    .setOrderExpireDatetime(expireDateTime)
                    .setSummary(withdrawDO.getAccountName() + "提现" + withdrawDO.getBalance());
            PayClient client = payClientFactory.getPayClient(payProperties.getChannelId());
            log.info("{}通商云提现入参{}", withdrawDO.getAccountName(), JsonUtils.toJsonString(applyReqDTO));
            PayCommonResult<WithdrawApplyRespDTO> result = client.withdrawApply(applyReqDTO);
            if (result.isSuccess()) {
                WithdrawApplyRespDTO resultData = result.getData();
                log.info("{}通商云提现返回参数:{}", withdrawDO.getAccountName(), JsonUtils.toJsonString(resultData));
                //修改状态
                financeThirdWithdrawMapper.updateStatusByTXNo(resultData.getBizOrderNo(), WithdrawStatusEnum.PROCESS.getName(),
                        FinanceThirdWithdrawDO.builder()
                                .thirdWithdrawNo(resultData.getOrderNo())
                                .build());
            } else {
                financeThirdWithdrawMapper.updateStatusByTXNo(withdrawDO.getWithdrawNo(), WithdrawStatusEnum.PROCESS.getName(),
                        FinanceThirdWithdrawDO.builder()
                                .status(WithdrawStatusEnum.FAILURE.getName())
                                .reason(result.getMsg())
                                .build());

            }
        } catch (Exception e) {
            log.error("通商云提现生异常：{}", e.getMessage());
        }

        return true;
    }

    @Override
    public PageResult<ThirdWithdrawRespVO> page(ThirdWithdrawQueryReqVO reqVO) {
        LambdaQueryWrapper<FinanceThirdWithdrawDO> wrapper = new LambdaQueryWrapper<FinanceThirdWithdrawDO>()
                .eq(StringUtils.isNotBlank(reqVO.getWithdrawNo()),FinanceThirdWithdrawDO::getWithdrawNo,reqVO.getWithdrawNo())
                .eq(StringUtils.isNotBlank(reqVO.getAccountName()),FinanceThirdWithdrawDO::getAccountName,reqVO.getAccountName())
                .ge(StringUtils.isNotBlank(reqVO.getWithdrawStartDate()),FinanceThirdWithdrawDO::getWithdrawTime,strToLocalDateTime(reqVO.getWithdrawStartDate().concat(" 00:00:00")))
                .le(StringUtils.isNotBlank(reqVO.getWithdrawEndDate()),FinanceThirdWithdrawDO::getWithdrawTime,strToLocalDateTime(reqVO.getWithdrawEndDate().concat(" 23:59:59")))
                .eq(StringUtils.isNotBlank(reqVO.getStatus()),FinanceThirdWithdrawDO::getStatus,reqVO.getStatus());
        PageResult<FinanceThirdWithdrawDO> pageResult = financeThirdWithdrawMapper.selectPage(reqVO, wrapper);
        List<ThirdWithdrawRespVO> collect = pageResult.getList().stream().map(t -> {
            ThirdWithdrawRespVO vo = new ThirdWithdrawRespVO();
            BeanUtils.copyProperties(t,vo);
            BigDecimal bigDecimal = Objects.nonNull(t.getBalance())?BigDecimal.valueOf(t.getBalance()):BigDecimal.ZERO;
            BigDecimal result = bigDecimal.divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
            vo.setBalance(result);
            vo.setStatus(TongLianRefundStatusEnum.getByCode(t.getStatus()).getDescribe());
            return vo;
        }).collect(Collectors.toList());
        PageResult<ThirdWithdrawRespVO> result = PageResult.empty();
        result.setList(collect);
        result.setTotal(pageResult.getTotal());
        return result;
    }

    @Override
    public List<ThirdWithdrawRespVO> list(ThirdWithdrawQueryReqVO reqVO) {
        LambdaQueryWrapper<FinanceThirdWithdrawDO> wrapper = new LambdaQueryWrapper<FinanceThirdWithdrawDO>()
                .eq(StringUtils.isNotBlank(reqVO.getWithdrawNo()),FinanceThirdWithdrawDO::getWithdrawNo,reqVO.getWithdrawNo())
                .eq(StringUtils.isNotBlank(reqVO.getAccountName()),FinanceThirdWithdrawDO::getAccountName,reqVO.getAccountName())
                .ge(StringUtils.isNotBlank(reqVO.getWithdrawStartDate()),FinanceThirdWithdrawDO::getWithdrawTime,strToLocalDateTime(reqVO.getWithdrawStartDate().concat(" 00:00:00")))
                .le(StringUtils.isNotBlank(reqVO.getWithdrawEndDate()),FinanceThirdWithdrawDO::getWithdrawTime,strToLocalDateTime(reqVO.getWithdrawEndDate().concat(" 23:59:59")))
                .eq(StringUtils.isNotBlank(reqVO.getStatus()),FinanceThirdWithdrawDO::getStatus,reqVO.getStatus());
         List<FinanceThirdWithdrawDO> list = financeThirdWithdrawMapper.selectList(wrapper);
        return  list.stream().map(t -> {
            ThirdWithdrawRespVO vo = new ThirdWithdrawRespVO();
            BeanUtils.copyProperties(t,vo);
            BigDecimal bigDecimal = Objects.nonNull(t.getBalance())?BigDecimal.valueOf(t.getBalance()):BigDecimal.ZERO;
            BigDecimal result = bigDecimal.divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
            vo.setBalance(result);
            vo.setStatus(TongLianRefundStatusEnum.getByCode(t.getStatus()).getDescribe());
            return vo;
        }).collect(Collectors.toList());
    }

    private LocalDateTime strToLocalDateTime(String time) {
        try {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            LocalDateTime dateTime = LocalDateTime.parse(time, formatter);
            return dateTime;
        } catch (Exception e) {
            log.error("日期转换失败！");
        }
        return null;
    }

}
